查看原文
其他

Keil、IAR实现处理器复位而变量不被初始化的方法

strongerHuang 嵌入式专栏 2022-09-10

关注+星标公众,不错过精彩内容

作者 | strongerHuang

微信公众号 | 嵌入式专栏


有时候,我们有这种需求:处理器复位,要求变量不被初始化。


比如:一个实时统计数据的系统,不想因为某种原因(异常原因死机、看门狗等)复位,而丢失正在计数而未来得及保存的数据。


下面就来讲讲在Keil、 IAR编译环境下,实现该功能的方法。


嵌入式专栏

1

处理器复位

处理器复位的方式有很多种,这里结合STM来讲述MCU复位的来源:


STM32的复位为三类:系统复位、电源复位和后备域复位


系统复位:

1. NRST引脚上的低电平(外部复位)

2. 窗口看门狗计数终止(WWDG复位)

3. 独立看门狗计数终止(IWDG复位)

4. 软件复位(SW复位)

5. 低功耗管理复位


电源复位:

1. 上电/掉电复位(POR/PDR复位)

2. 从待机模式中返回


备份区域复位:

1. 软件复位,备份区域复位可由设置备份域控制寄存器(RCC_BDCR)中的BDRST位产生。

2. 在VDD和VBAT两者掉电的前提下, VDD或VBAT上电将引发备份区域复位。


请参考《STM32复位来源,Cotrex-M系统与内核复位区别》。


嵌入式专栏

2

修饰符

实现处理器复位而变量不被初始化方法之前,让我们先了解一下修饰符的知识。


修饰符是用于限定类型以及类型成员申明的一种符号。如C语言中常见的修饰符:

1.static静态修饰符:修饰变量,函数。作用域:变量仅仅在本文件可见,函数在本文件可以被调用;

2.extern声明修饰符:修饰变量,函数。修饰变量时候,变量的声明在外面;

3.const常量修饰符:修饰变量,函数。修饰变量时候,不能被重复赋值,只能放在只读段中;

4.volatile不稳定变量修饰符:这个变量不好翻译,在c中的作用大概有两点意思:A.表示变量是易失的,易变的; B.强制访存操作,防止编译器去优化,告诉编译器每次必须去内存中取值,而不是从寄存器或者缓存。

其实,在C++  JAVA中还有更多:
public公共访问修饰符、private私有访问修饰符、protected保护访问修饰符、friendly、abstract等。

而本文会使用到一个修饰符:
__no_init

虽然这个修饰符不是C语言标准的修饰符,但在Keil、IAR这种集成开发环境中,他们支持这种修饰符。

而本文说的修饰符,修饰的变量位于RAM中:
在默认情况下,编译器会将其变量存放在主RAM中,并在启动时对其进行初始化。而__no_init类型修饰符使编译器把变量放在非易失RAM区中,在启动时也不对它们进行初始化,也就是说__no_init在系统启动时不初始化变量。

嵌入式专栏

3

Keil中__no_init的配置和使用
在Keil中,__no_init不是标准的修饰符,需要进行配置,配置之后就可以使用了。

1.宏定义__no_init
#define __no_init __attribute__((zero_init))

2.在工程选项中配置__no_init
Project -> Options for Targets -> Target,里面右下有个NoInit,这个就是需要我们配置的区域(可设定某一区域);


3.使用方法
比如定义变量:Cnt_NoInit
__no_init uint16_t Cnt_NoInit;

提示:不能初始化这个变量(也就是定义时不要赋值)。

嵌入式专栏

4

IAR中中使用__no_init

在IAR中“__no_init”属于是一个关键字,你会发现在使用这个修饰符之后,字体都是关键字颜色。


直接使用即可,类似上面定义一个不被初始化的变量:

__no_init uint16_t Cnt_NoInit;

嵌入式专栏

5

参考源码
为方便大家学习,这里分享源代码下载地址:
http://pan.baidu.com/s/1hskScba
(公号不支持外链接,请复制链接到浏览器下载)

两个工程名称:
1.STM32F103ZE(Keil)_复位不初始化变量NoInit
2.STM32F103ZE(IAR)_复位不初始化变量NoInit

本文提供的例程是一个Demo比较简单,Keil和IAR工程实现的功能一样。

源代码:
__no_inituint16_t Cnt_NoInit;uint16_t Cnt_Init = 100;
int main(void){ System_Initializes(); printf("Start...\n"); //复位打印
while(1) { printf("Cnt_NoInit = %d\n", Cnt_NoInit); //打印变量 Cnt_NoInit++;
if(Cnt_NoInit > 1000) { Cnt_NoInit = 0; }
printf("Cnt_Init = %d\n", Cnt_Init); Cnt_Init++; if(Cnt_Init > 1000) { Cnt_Init = 0; }
LED_ON; TIMDelay_Nms(500); LED_OFF; TIMDelay_Nms(500); NVIC_SystemReset(); //系统复位 }}


被Cnt_NoInit修饰,则会打印如下消息:
Start...Cnt_NoInit = 0Cnt_Init = 100
Start...Cnt_NoInit = 1Cnt_Init = 100
Start...Cnt_NoInit = 2Cnt_Init = 100
Start...Cnt_NoInit = 3Cnt_Init = 100


如果不被修饰:
uint16_t Cnt_NoInit;uint16_t Cnt_Init = 100;
如果不被修饰:则会打印如下消息:
Start...Cnt_NoInit = 0Cnt_Init = 100
Start...Cnt_NoInit = 0Cnt_Init = 100
Start...Cnt_NoInit = 0Cnt_Init = 100
Start...Cnt_NoInit = 0Cnt_Init = 100

相信聪明的你,看了上面例子会明白为什么没有初始化的变量“Cnt_NoInit”在变化,而初始化了的“Cnt_Init”一直不变。

------------ END ------------


后台回复『嵌入式软件设计与开发』『Keil』『IAR』阅读更多相关文章。


欢迎关注我的公众号回复“加群”按规则加入技术交流群,回复“1024”查看更多内容。

欢迎关注我的视频号:


点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存